GtkApplication: Extract accelerators from menus
authorMatthias Clasen <mclasen@redhat.com>
Mon, 5 Dec 2011 22:41:56 +0000 (17:41 -0500)
committerRyan Lortie <desrt@desrt.ca>
Mon, 19 Dec 2011 17:51:11 +0000 (12:51 -0500)
This allows to specify accelerators in a convenient way, by
adding accel="..." attributes to the menu items in the
XML for the menu.

Currently, the accelerators are extracted only once, when the
menu property is changed on the application.

gtk/gtkapplication.c

index 3ba6bf1ee9f79d2eab3eb31efc5f432b203c68f1..6dd281831321620805cf1ffef6c03c22c18ccfb8 100644 (file)
@@ -347,11 +347,89 @@ gtk_application_window_removed (GtkApplication *application,
   gtk_window_set_application (window, NULL);
 }
 
+static void
+extract_accel_from_menu_item (GMenuModel     *model,
+                              gint            item,
+                              GtkApplication *app)
+{
+  GMenuAttributeIter *iter;
+  const gchar *key;
+  GVariant *value;
+  const gchar *accel = NULL;
+  const gchar *action = NULL;
+  GVariant *target = NULL;
+
+  iter = g_menu_model_iterate_item_attributes (model, item);
+  while (g_menu_attribute_iter_get_next (iter, &key, &value))
+    {
+      if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+        action = g_variant_get_string (value, NULL);
+      else if (g_str_equal (key, "accel") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+        accel = g_variant_get_string (value, NULL);
+      else if (g_str_equal (key, "target"))
+        target = g_variant_ref (value);
+      g_variant_unref (value);
+    }
+  g_object_unref (iter);
+
+  if (accel && action)
+    gtk_application_add_accelerator (app, accel, action, target);
+
+  if (target)
+    g_variant_unref (target);
+}
+
+static void
+extract_accels_from_menu (GMenuModel     *model,
+                          GtkApplication *app)
+{
+  gint i;
+  GMenuLinkIter *iter;
+  const gchar *key;
+  GMenuModel *m;
+
+  for (i = 0; i < g_menu_model_get_n_items (model); i++)
+    {
+      extract_accel_from_menu_item (model, i, app);
+
+      iter = g_menu_model_iterate_item_links (model, i);
+      while (g_menu_link_iter_get_next (iter, &key, &m))
+        {
+          extract_accels_from_menu (m, app);
+          g_object_unref (m);
+        }
+      g_object_unref (iter);
+    }
+}
+
+static void
+gtk_application_notify (GObject    *object,
+                        GParamSpec *pspec)
+{
+  if (strcmp (pspec->name, "app-menu") == 0 ||
+      strcmp (pspec->name, "menubar") == 0)
+    {
+      GMenuModel *model;
+      g_object_get (object, pspec->name, &model, NULL);
+      if (model)
+        {
+          extract_accels_from_menu (model, GTK_APPLICATION (object));
+          g_object_unref (model);
+        }
+    }
+
+  if (G_OBJECT_CLASS (gtk_application_parent_class)->notify)
+    G_OBJECT_CLASS (gtk_application_parent_class)->notify (object, pspec);
+}
+
 static void
 gtk_application_class_init (GtkApplicationClass *class)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
   GApplicationClass *application_class = G_APPLICATION_CLASS (class);
 
+  object_class->notify = gtk_application_notify;
+
   application_class->add_platform_data = gtk_application_add_platform_data;
   application_class->before_emit = gtk_application_before_emit;
   application_class->after_emit = gtk_application_after_emit;